package edu.fiu.cis.personalize.server.graph.store;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

import edu.fiu.cis.personalize.server.GraphUpdater;
import edu.fiu.cis.personalize.server.UpdateElement;
import edu.fiu.cis.personalize.server.graph.Document;
import edu.fiu.cis.personalize.server.graph.Group;
import edu.fiu.cis.personalize.server.graph.Node;
import edu.fiu.cis.personalize.server.graph.NodeType;
import edu.fiu.cis.personalize.server.graph.Query;
import edu.fiu.cis.personalize.server.graph.User;
import edu.fiu.cis.personalize.server.objectrank.NodeRef;

public class NodeStore {

	private HashMap<NodeType, NodeCache<? extends Node>> cache = new HashMap<NodeType, NodeCache<? extends Node>>();

	private HashSet<NodeRef> allRefs = new HashSet<NodeRef>();

	private Object all_ref_semaphore = new Object();

	public ArrayList<NodeRef> getAllNodeRefs() {
		synchronized (all_ref_semaphore) {
			return new ArrayList<NodeRef>(allRefs);
		}
	}

	private NodeStore() {
		cache.put(NodeType.DOCUMENT, new NodeCache<Document>() {

			@Override
			public Document create(String URL) {
				return new Document(nextId(), URL);
			}
		});
		cache.put(NodeType.QUERY, new NodeCache<Query>() {

			@Override
			public Query create(String stringKey) {
				return new Query(nextId(), stringKey);
			}
		});
		cache.put(NodeType.GROUP, new NodeCache<Group>() {

			@Override
			public Group create(String groupDescription) {
				return new Group(nextId(), groupDescription);
			}
		});
		cache.put(NodeType.USER, new NodeCache<User>() {

			@Override
			public User create(String userName) {
				return new User(nextId(), userName);
			}
		});
	}

	public Node get(int id, NodeType type) {
		return cache.get(type).get(id);
	}

	public void add(Node n) {
		allRefs.add(new NodeRef(n.getId(), n.getType()));
		cache.get(n.getType()).add(n);
	}

	public Node get(String id, NodeType type, boolean create) {
		Node n = cache.get(type).get(id);
		if (n == null && create) {
			n = create(id, type);
			n.setNew(true);
			GraphUpdater.enque(new UpdateElement(n.getId(), n.getType()));
			
			synchronized (all_ref_semaphore) {
				add(n);
			}
		}
		return n;
	}

	protected Node create(String str, NodeType nodeType) {
		return cache.get(nodeType).create(str);
	}

	public User getUser(int id) {
		return (User) get(id, NodeType.USER);
	}

	public Query getQuery(int id) {
		return (Query) get(id, NodeType.QUERY);
	}

	@SuppressWarnings("unchecked")
	public NodeCache<User> getAllUsers() {
		return (NodeCache<User>) cache.get(NodeType.USER);
	}

	@SuppressWarnings("unchecked")
	public NodeCache<Query> getAllQueries() {
		return (NodeCache<Query>) cache.get(NodeType.QUERY);
	}

	@SuppressWarnings("unchecked")
	public NodeCache<Document> getAllDocuments() {
		return (NodeCache<Document>) cache.get(NodeType.DOCUMENT);
	}

	@SuppressWarnings("unchecked")
	public NodeCache<Group> getAllGroups() {
		return (NodeCache<Group>) cache.get(NodeType.GROUP);
	}

	public static NodeStore create() {
		return new NodeStore();
	}

	public void load(Node u) {
		cache.get(u.getType()).add(u);
	}

	public void loadKey(NodeType type, int value) {
		cache.get(type).setCounterValue(value);
	}

	public int getCounterValue(NodeType type) {
		return cache.get(type).getCounterValue();
	}

}
